


\subsubsubsection{25.1.1\hspace{0.2cm}存储}

元组包含模板参数列表中每个类型的存储空间，可以通过函数模板get来访问，用作元组t的get<I>(t)。例如，在前面的例子中，t上的get<0>(t)将返回对int 17的引用，而get<1>(t)将返回对double 3.14的引用。

元组存储的递归公式基于这样一种思想:包含N(>0)个元素的元组，既可以存储为单个元素(列表的第一个元素或头元素)，也可以存储为包含N - 1个元素的元组(列表的尾部)，对于空元组有单独的处理。因此，三元组tuple<int, double, std::string>可以存储为int类型，而tuple<double, std::string>可以存储为三元组，包含两个元素的元组可以存储为double类型和tuple<std::string>类型，其本身可以存储为std::string类型和Tuple<>类型。这与通用版本的类型列表算法中使用的递归分解相同，递归元组存储的实现过程也类似:

\hspace*{\fill} \\ %插入空行
\noindent
\textit{typelist/tuple0.hpp}
\begin{lstlisting}[style=styleCXX]
template<typename... Types>
class Tuple;

// recursive case:
template<typename Head, typename... Tail>
class Tuple<Head, Tail...>
{
	private:
	Head head;
	Tuple<Tail...> tail;
	public:
	// constructors:
	Tuple() {
	}
	Tuple(Head const& head, Tuple<Tail...> const& tail)
	: head(head), tail(tail) {
	}
	...
	
	Head& getHead() { return head; }
	Head const& getHead() const { return head; }
	Tuple<Tail...>& getTail() { return tail; }
	Tuple<Tail...> const& getTail() const { return tail; }
};

// basis case:
template<>
class Tuple<> {
	// no storage required
};
\end{lstlisting}

递归时，每个Tuple实例包含存储列表中第一个元素的数据成员头部，以及存储列表中剩余元素的数据成员尾部。一般空元组，没有相关的存储。

get函数模板遍历这个递归结构来提取相应的元素:

\begin{tcolorbox}[colback=webgreen!5!white,colframe=webgreen!75!black]
\hspace*{0.75cm}get()的完整实现还应该处理非常量和右值引用元组。
\end{tcolorbox}

\hspace*{\fill} \\ %插入空行
\noindent
\textit{typelist/tupleget.hpp}
\begin{lstlisting}[style=styleCXX]
// recursive case:
template<unsigned N>
struct TupleGet {
	template<typename Head, typename... Tail>
	static auto apply(Tuple<Head, Tail...> const& t) {
		return TupleGet<N-1>::apply(t.getTail());
	}
};

// basis case:
template<>
struct TupleGet<0> {
	template<typename Head, typename... Tail>
	static Head const& apply(Tuple<Head, Tail...> const& t) {
		return t.getHead();
	}
};

template<unsigned N, typename... Types>
auto get(Tuple<Types...> const& t) {
	return TupleGet<N>::apply(t);
}
\end{lstlisting}

函数模板get只是对TupleGet的静态成员函数使用的一个简单包装，有效地解决了函数模板缺乏偏特化的问题(在第17.3节中讨论)，我们使用它特化N的值。在递归情况下(N > 0)，静态成员函数apply()可取当前元组的尾部，并递减N以继续寻找元组中稍后请求的元素。一般情况会(N = 0)返回当前元组的头部。

\subsubsubsection{25.1.2\hspace{0.2cm}构造}

除了目前定义的构造函数之外:

\begin{lstlisting}[style=styleCXX]
Tuple() {
}

Tuple(Head const& head, Tuple<Tail...> const& tail)
: head(head), tail(tail) {
}
\end{lstlisting}

要使元组有效，需要能够从一组独立的值(每个元素一个值)和另一个元组中进行构造。复制构造从一组独立的值传递第一个值来初始化头部元素(通过基类)，然后将剩余的值传递给基类，表示尾部:

\begin{lstlisting}[style=styleCXX]
Tuple(Head const& head, Tail const&... tail)
: head(head), tail(tail...) {
}
\end{lstlisting}

初始化元组:

\begin{lstlisting}[style=styleCXX]
Tuple<int, double, std::string> t(17, 3.14, "Hello, World!");
\end{lstlisting}

这并不是最通用的接口:用户可能希望使用移动构造来初始化部分(但可能不是全部)元素，或者使用不同类型的值构造。因此，应该使用完美转发(第15.6.3节)来初始化元组:

\begin{lstlisting}[style=styleCXX]
template<typename VHead, typename... VTail>
Tuple(VHead&& vhead, VTail&&... vtail)
	: head(std::forward<VHead>(vhead)),
		tail(std::forward<VTail>(vtail)...) {
}
\end{lstlisting}

接下来，实现使用一个元组构造另一个元组:

\begin{lstlisting}[style=styleCXX]
template<typename VHead, typename... VTail>
Tuple(Tuple<VHead, VTail...> const& other)
: head(other.getHead()), tail(other.getTail()) { }
\end{lstlisting}

然而，这个构造函数还不足以允许元组转换:给定上面的元组t，尝试创建另一个具有兼容类型的元组将会失败:

\begin{lstlisting}[style=styleCXX]
// ERROR: no conversion from Tuple<int, double, string> to long
Tuple<long int, long double, std::string> t2(t);
\end{lstlisting}

这里的问题是，用于从一组独立值进行初始化的构造函数模板，比接受元组的构造函数模板更匹配。为了解决这个问题，当尾部没有预期的长度时，必须使用std::enable\_if<>(参见第6.3节和第20.3节)来禁用两个成员函数模板:

\begin{lstlisting}[style=styleCXX]
template<typename VHead, typename... VTail,
		typename = std::enable_if_t<sizeof...(VTail)==sizeof...(Tail)>>
Tuple(VHead&& vhead, VTail&&... vtail)
: head(std::forward<VHead>(vhead)),
tail(std::forward<VTail>(vtail)...) { }

template<typename VHead, typename... VTail,
		typename = std::enable_if_t<sizeof...(VTail)==sizeof...(Tail)>>
Tuple(Tuple<VHead, VTail...> const& other)
: head(other.getHead()), tail(other.getTail()) { }
\end{lstlisting}

可以在tuple/tuple.hpp中找到所有构造函数的声明。

makeTuple()函数模板使用推导来确定返回元组的元素类型，使得给定的元素集创建元组变得相对容易许多:

\hspace*{\fill} \\ %插入空行
\noindent
\textit{typelist/maketuple.hpp}
\begin{lstlisting}[style=styleCXX]
template<typename... Types>
auto makeTuple(Types&&... elems)
{
	return Tuple<std::decay_t<Types>...>(std::forward<Types>(elems)...);
}
\end{lstlisting}

再次使用完美转发与std::decay<>特征相结合，将字字面值和其他原始数组转换成指针，并移除const和引用限定。

\begin{lstlisting}[style=styleCXX]
makeTuple(17, 3.14, "Hello, World!")
\end{lstlisting}

初始化

\begin{lstlisting}[style=styleCXX]
Tuple<int, double, char const*>
\end{lstlisting}







